10 个最终编译成 JavaScript 的脚本语言
写作翻译▼
英文原文:10 Languages That Compile to JavaScript
链接:https://www.sitepoint.com/10-languages-compile-javascript/
译者:圣洁之子, Tocy, 无若, brookstar
与简单的网站相比,现代应用程序有更多不同的需求。但是,浏览器是一个拥有(大部分)固定技术的平台,而且JavaScript仍然是web应用程序的核心语言;需要在浏览器中运行的任何应用程序都必须用该语言实现。
我们都知道JavaScript并不是实现所有任务的最佳语言,当遇到复杂的应用时,它可能就不太适合。为了避免这个问题,已经创建了几种新的语言和现有语言的变体,它们都生成可以在浏览器中运行的代码,而无需编写任何JavaScript代码,也不用考虑语言的局限性。
文中介绍了10个有趣的语言,这些语言都可以转换成JavaScript代码在浏览器中执行,也可以在Node.js这样的平台上执行。
Dart
Dart 是一种经典的面向对象语言,其中所有事物都是对象,任何对象都是类的实例(对象也可以充当函数)。它专门用于构建浏览器、服务器和移动设备的应用程序。它由谷歌维护,并且是驱动下一代 AdWords 用户界面的语言, 而 AdWords 是给谷歌创造收入的最重要的产品,这本身就是在证明其大规模的力量。
该语言可被翻译成 JavaScript ,以便在浏览器中使用,或者由 Dart VM 直接解释执行,这也允许你构建服务器应用程序。可以使用 Flutter SDK 创建移动应用程序。
复杂的应用程序还需要一组成熟的库和专门为此任务而设计的语言特性,而 Dart 包含所有这些库和特性。一个流行的库的例子是 AngularDart,Angular 的 Dart 版本。
它允许你编写类型安全的代码,而不会太有侵入性;你可以编写类型,但你不需要这样做,因为它们可以被推断出来。这允许创建快速原型,而不必过度考虑细节,但一旦你的原型可以工作,你可以添加类型以使其更加健壮。
关于虚拟机中的并发编程,Dart 使用所谓的 Isolates 连同它们自己的内存堆,来代替共享内存线程(Dart 是单线程的),使用消息实现通信。 在浏览器中,这个事件稍有不同:你并不是创建新的 isolates, 而是创建新的 Workers 。
// Example extracted from dartlang.org
import 'dart:async';
import 'dart:math' show Random;
main() async {
print('Compute π using the Monte Carlo method.');
await for (var estimate in computePi()) {
print('π ≅ $estimate');
}
}
/// Generates a stream of increasingly accurate estimates of π.
Stream<double> computePi({int batch: 1000000}) async* {
var total = 0;
var count = 0;
while (true) {
var points = generateRandom().take(batch);
var inside = points.where((p) => p.isInsideUnitCircle);
total += batch;
count += inside.length;
var ratio = count / total;
// Area of a circle is A = π⋅r², therefore π = A/r².
// So, when given random points with x ∈ <0,1>,
// y ∈ <0,1>, the ratio of those inside a unit circle
// should approach π / 4. Therefore, the value of π
// should be:
yield ratio * 4;
}
}
Iterable<Point> generateRandom([int seed]) sync* {
final random = new Random(seed);
while (true) {
yield new Point(random.nextDouble(), random.nextDouble());
}
}
class Point {
final double x, y;
const Point(this.x, this.y);
bool get isInsideUnitCircle => x * x + y * y <= 1;
}
TypeScript
TypeScript 是 JavaScript 的超集;有效的 JavaScript 程序也是有效的 TypeScript 程序,但后者添加了静态类型。其编译器也可以充当从 ES2015+ 到当前实现的转译器,所以你总能获得最新的功能。
不同于许多其他语言,TypeScript 保持 JavaScript 的精神完好无缺,只增加了功能来提高代码的可靠性。这些是类型注释,以及其他类型相关的功能,使得编写 JavaScript 更加愉快,这得益于启用了静态分析器等专门的工具来帮助重构过程。另外,类型的添加改进了应用程序不同组件之间的接口。
类型推断受到支持,因此你不必从一开始就编写所有的类型。你可以编写快速解决方案,然后添加所有的类型以获取对代码的信心。
TypeScript 也支持高级的类型,如 intersection(交集) 类型 、 union(并集、联合) 类型、 类型别名、 可区分联合(discriminated union)和类型保护。你可以在 TypeScript 文档站点的高级类型页面中查看所有这些类型。
如果你使用 React,则还可以通过添加 React 类型来支持 JSX。
class Person {
private name: string;
private age: number;
private salary: number;
constructor(name: string, age: number, salary: number) {
this.name = name;
this.age = age;
this.salary = salary;
}
toString(): string {
return `${this.name} (${this.age}) (${this.salary})`;
}
}
Elm
Elm 是一种纯粹的函数编程语言,它编译成 JS、 HTM L和 CSS。 你可以只使用 Elm 构建一个完整的站点。这使之成为 JavaScript 框架如 React 的一个很好的替代方案。用它构建的应用程序会自动使用虚拟 DOM 库,使其速度非常快。其一大优点是内置的体系结构,它使你忘记了数据流,而将重点放在数据声明和逻辑上。
在 Elm 中,所有函数都是纯粹的,这意味着它们将总是对给定输入返回相同的输出。除非你指定,否则它们不能做任何其他事情。例如,为了访问远程 API,你将创建 command (命令)函数来与外界通信,以及创建 subscription (订阅)以侦听响应。纯粹性的另一点是,值是不可变的;当你需要某样东西时,你创建新值,而不是修改旧值。
Elm 的采用可以是渐进式的;可以使用 ports (端口) 与 JavaScript 和其他库进行通信。虽然 Elm 尚未达到版本 1,但它正被用于复杂和大型的应用程序,使其成为复杂的应用程序的可行的解决方案。
Elm 最具吸引力的功能之一是初学者友好的编译器,不是生成难以阅读的消息,而是生成代码,可以帮助你修复你的代码。如果你在学习该语言,编译器本身就可以大有助益。
module Main exposing (..)
import Html exposing (..)
-- MAIN
main : Program Never Model Msg
main =
Html.program
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
-- INIT
type alias Model = String
init : ( Model, Cmd Msg )
init = ( "Hello World!", Cmd.none )
-- UPDATE
type Msg
= DoNothing
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
DoNothing ->
( model, Cmd.none )
-- VIEW
view : Model -> Html Msg
view model =
div [] [text model]
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
PureScript
PureScript 是由 Phil Freeman 创建的纯函数式强类型的编程语言。它旨在给可用的 JavaScript 库提供强大的兼容性,在精神上类似于 Haskell,但保持 JavaScript 的核心。
PureScript 的一个强项是它的极简主义。它不包括在其他语言中被认为是必需的功能的任何库。例如,不是编译器本身包含生成器和 promises,而是你可以使用特定的库来完成任务。你可以为所需功能选择想要的实现,这样可以在使用 PureScript 时实现高效和个性化的体验,同时保持生成的代码尽可能小。
其编译器的另一个显著特征就是能够在保持与 JavaScript 的兼容性的同时, 用库和工具生成整洁和可读的代码。
像其他语言一样,PureScript 有自己的构建工具叫做 Pulp,可以与 Gulp 进行比较, 但是用于以这种语言编写的项目。
关于类型系统,与 Elm不同,即另一种 ML 式的语言,PureScript 支持高级类型的功能,如取自 Haskell 的 higher-kinded types(高级类类型) 以及 type classes(类型类), 从而允许创建复杂的抽象。
module Main where
import Prelude
import Data.Foldable (fold)
import TryPureScript
main =
render $ fold
[ h1 (text "Try PureScript!")
, p (text "Try out the examples below, or create your own!")
, h2 (text "Examples")
, list (map fromExample examples)
]
where
fromExample { title, gist } =
link ("?gist=" <> gist) (text title)
examples =
[ { title: "Algebraic Data Types"
, gist: "37c3c97f47a43f20c548"
}
, { title: "Loops"
, gist: "cfdabdcd085d4ac3dc46"
}
, { title: "Operators"
, gist: "3044550f29a7c5d3d0d0"
}
]
CoffeeScript
CoffeeScript 是一种语言,旨在公开 JavaScript 的良好部分,同时提供更整洁的语法并保留语义。虽然该语言的流行度近年来一直在减弱,但它正在改变方向,现在正在获得一个新的主要版本,为 ES2015+ 的功能提供支持。
你用 CoffeeScript 编写的代码被直接翻译为可读的 JavaScript 代码,并保持与现有库的兼容性。从版本 2 开始,编译器将产生与最新版本的 ECMAScript 兼容的代码。例如,每次你使用一个类,你就获得一个 JavaScript 类。另外,如果你使用 React,也有好消息: JSX 与 CoffeeScript 兼容。
其编译器的一个非常显著的特征是能够处理以 literate style(识字风格)编写的代码, 与强调代码并将注释额外添加不同,你首先需要编写注释,而代码只会偶尔出现。这种编程风格由 Donald Knuth 引进,使得代码文件与技术文章非常相似。
与其他语言不同,CoffeeScript 的代码可以直接在浏览器中以库解释。所以如果你想创建一个快速测试,你可以在 text/coffeescript 脚本标签里编写你的代码,并包括编译器,它将把你的代码动态地翻译为 JavaScript 。
# Assignment:
number = 42
opposite = true
# Conditions:
number = -42 if opposite
# Functions:
square = (x) -> x * x
# Arrays:
list = [1, 2, 3, 4, 5]
# Objects:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
# Splats:
race = (winner, runners...) ->
print winner, runners
# Existence:
alert "I knew it!" if elvis?
# Array comprehensions:
cubes = (math.cube num for num in list)
ClojureScript
ClojureScript 是将 Clojure 编程语言转换为 JavaScript 的编译器。它是一种通用的函数式语言,支持动态类型和不可变数据结构。
它是这个列表中唯一属于 Lisp 家族系列的编程语言,当然,它拥有很多功能。 例如,代码可以被视为数据,并且支持宏系统,这使得元编程技术成为可能。与其他 Lisps 不同的是,Clojure 支持不可变的数据结构,使副作用的管理更加容易。
对于新手来说,它的语法使用括号可能看起来很恐怖,但是这样做有着深刻的意义,从长远来看,你一定会感谢这种做法。语法上的简约性和语法抽象能力使 Lisp 成为解决那些需要高抽象层次的问题的强大工具。
虽然 Clojure 主要是函数式语言,但它不像 PureScript 或 Elm 那样纯粹;副作用仍然可以发生,但其他函数式特征仍然存在。
ClojureScript 使用 Google Closure 进行代码优化,并且与现有的 JavaScript 库兼容。
; Extracted from https://github.com/clojure/clojurescript/blob/master/samples/dom/src/dom/test.cljs
(ns dom.test
(:require [clojure.browser.event :as event]
[clojure.browser.dom :as dom]))
(defn log [& args]
(.log js/console (apply pr-str args)))
(defn log-obj [obj]
(.log js/console obj))
(defn log-listener-count []
(log "listener count: " (event/total-listener-count)))
(def source (dom/get-element "source"))
(def destination (dom/get-element "destination"))
(dom/append source
(dom/element "Testing me ")
(dom/element "out!"))
(def success-count (atom 0))
(log-listener-count)
(event/listen source
:click
(fn [e]
(let [i (swap! success-count inc)
e (dom/element :li
{:id "testing"
:class "test me out please"}
"It worked!")]
(log-obj e)
(log i)
(dom/append destination
e))))
(log-obj (dom/element "Text node"))
(log-obj (dom/element :li))
(log-obj (dom/element :li {:class "foo"}))
(log-obj (dom/element :li {:class "bar"} "text node"))
(log-obj (dom/element [:ul [:li :li :li]]))
(log-obj (dom/element :ul [:li :li :li]))
(log-obj (dom/element :li {} [:ul {} [:li :li :li]]))
(log-obj (dom/element [:li {:class "baz"} [:li {:class "quux"}]]))
(log-obj source)
(log-listener-count)
Scala.js
Scala.js 是将 Scala 编程语言转换成 JavaScript 语言的编译器。Scala 语言的目标是合并面向对象编程和函数式编程到一种语言中来,并创建一套强有力的易于使用的工具。
作为一种强类型语言,你可以在灵活的类型系统中获取局部类型推断的好处。其最大的价值在于可以被推测,但是它的函数参数还是需要强制类型说明。
虽然支持许多常见的面向对象模式(例如,每个值都是一个对象,操作是方法调用),但你也可以获得函数特性,如支持一级函数和不可变数据结构。
Scala.js 的一个特殊优点是,你可以从熟悉的面向对象方式开始,并且按需要以自己的速度移动到更加函数的方式,而无需做大量的工作。此外,现有的 JavaScript 代码和库与你的 Scala 代码兼容。
新手 Scala 开发人员会发现这门语言与 JavaScript 没有太大差异,比较下面的等效代码:
// JavaScriptvar xhr = new XMLHttpRequest();xhr.open("GET",
"https://api.twitter.com/1.1/search/" +
"tweets.json?q=%23scalajs");xhr.onload = (e) => {
if (xhr.status === 200) {
var r = JSON.parse(xhr.responseText);
$("#tweets").html(parseTweets(r));
}};xhr.send();
// Scala.js
val xhr = new XMLHttpRequest()
xhr.open("GET",
"https://api.twitter.com/1.1/search/" +
"tweets.json?q=%23scalajs"
)
xhr.onload = { (e: Event) =>
if (xhr.status == 200) {
val r = JSON.parse(xhr.responseText)
$("#tweets").html(parseTweets(r))
}
}
xhr.send()
Reason
Reason 是由 Facebook 创建和维护的语言,它为 OCaml 编译器提供了新的语法,其代码可以转换为 JavaScript 和原生代码。
作为 ML 家族系列的一部分,以及它本身是函数式语言,它自然地提供了强大但灵活的类型系统,支持自动类型推断、代数数据类型和模式匹配。它还支持不可变的数据类型和参数多态(在其他语言中也被称为泛型),但是与 OCaml 一样,它也支持面向对象编程。
绑定 bucklescript 之后可以使用现有的 JavaScript 库。你也可以将 JavaScript 与 Reason 代码混合在一起使用。插入的 JavaScript 代码将不会被严格检查,但它可以很好地适用于快速修复或原型。
如果你是 React 开发人员,则可以使用 binding,该语言也支持 JSX。
/* A type variant being pattern matched */
let possiblyNullValue1 = None;
let possiblyNullValue2 = Some "Hello@";
switch possiblyNullValue2 {
| None => print_endline "Nothing to see here."
| Some message => print_endline message
};
/* Parametrized types */
type universityStudent = {gpa: float};
type response 'studentType = {status: int, student: 'studentType};
let result: response universityStudent = fetchDataFromServer ();
/* A simple typed object */
type payload = Js.t {.
name: string,
age: int
};
let obj1: payload = {"name": "John", "age": 30};
Haxe
Haxe 是一门多范式的编程语言,其编译器可以生成其他语言的二进制代码和源代码。
虽然 Haxe 提供了一个严格的类型系统,支持类型推断,但如果目标语言支持,它也可以作为动态语言。以同样的方式,它支持各种编程风格,如面向对象、泛型和函数式。
当你编写 Haxe 代码,你可以针对多种平台和语言进行编译,而无需进行相当大的改动。目标特定的代码块也可用。
你可以使用相同的代码在 Haxe 中编写后端和前端,并通过 Haxe Remoting 为同步或异步连接实现通信。
正如预期的那样,Haxe 代码与现有库兼容,但也提供了一个成熟的标准库。
// Example extracted from http://code.haxe.org
extern class Database {
function new();
function getProperty<T>(property:Property<T>):T;
function setProperty<T>(property:Property<T>, value:T):Void;
}
abstract Property<T>(String) {
public inline function new(name) {
this = name;
}
}
class Main {
static inline var PLAYER_NAME = new Property<String>("playerName");
static inline var PLAYER_LEVEL = new Property<Int>("playerLevel");
static function main() {
var db = new Database();
var playerName = db.getProperty(PLAYER_NAME);
trace(playerName.toUpperCase());
db.setProperty(PLAYER_LEVEL, 1);
}
}
Nim
Nim 是静态类型的、简约和空白敏感语法的多范式编程语言,编译为 C、 C++ 和 JavaScript。
该语言本身很小,但它的元编程能力使它吸引你亲自去实现其他语言内置的功能。它的元编程能力的“积木”是宏、模板和泛型,利用它们你可以完成从小功能到不同的范式的实现。这使得 Nim 成为一种非常通用的语言,可以用 Lisp 的精神来适应你的需要。
Nim 的句法抽象特性允许你针对你的问题改编该语言,使真正的 DSL(Domain Specific Language,领域特定语言) 成为可能。如果你有专门的任务要解决,你可以得到更高层次的表达能力。
# Reverse a string
proc reverse(s: string): string =
result = ""
for i in countdown(high(s), 0):
result.add s[i]
var str1 = "Reverse This!"
echo "Reversed: ", reverse(str1)
# Using templates
template genType(name, fieldname: expr, fieldtype: typedesc) =
type
name = object
fieldname: fieldtype
genType(Test, foo, int)
var x = Test(foo: 4566)
echo(x.foo) # 4566
如果 JavaScript 不是你最喜欢的语言,你也可以创建 Web 应用程序,而不必忍受该技术的缺点。用于创建这些应用程序的选项可以满足广泛的品味,从纯函数式语言(如 PureScript)到面向对象的语言(如 Dart)。如果你想要的不仅仅是一对一的语言翻译,你可以选择像 Elm 这样的为你提供虚拟 DOM 和内置架构工具的语言。
你是否尝试过本文中的某一种语言呢?是否有什么其他推荐的? 欢迎留言!